#include "Protocol.h"

bool Request::Deserialize(string &buffer)
{
    size_t pos = 0;
    // 解析请求行
    string line;
    if (Util::ReadLine(buffer, pos, line) == -1)
        return false;
    std::stringstream ss(line);
    ss >> _method >> _uri >> _version;
    _url = _uri;
    std::transform(_method.begin(), _method.end(), _method.begin(), toupper);

    // 解析首部字段
    string key, value;
    while (true)
    {
        line.clear();
        int n = Util::ReadLine(buffer, pos, line);
        if (n == 0) // 空行
            break;
        else if (n == -1)
            return false;
        if (!Util::CutString(line, key, value, ": "))
            return false;
        _header.insert({key, value});
    }

    // 解析请求体
    auto iter = _header.find("Content-Length");
    if (iter != _header.end())
    {
        size_t len = stoi(iter->second);
        if (buffer.size() - pos < len)
            return false;
        _content = buffer.substr(pos, len);
        pos += len;
    }
    buffer.erase(0, pos);
    return true;
}

void Request::DebugPrint()
{
    cout << "method: " << _method << endl;
    cout << "uri: " << _uri << endl;
    cout << "url: " << _url << endl;
    cout << "query_string: " << _queryStr << endl;
    cout << "header:" << endl;
    for (auto &[k, v] : _header)
    {
        cout << "\t" << k << ": " << v << endl;
    }
    cout << "content: " << endl;
    cout << _content;
    cout << endl;
}

string Response::Serialize()
{
    std::stringstream ss;
    ss << _version << " " << _stateCode << " " << _stateDesc << CRLF;
    for (auto &[k, v] : _header)
    {
        ss << k << ": " << v << CRLF;
    }
    ss << CRLF;
    return ss.str();
}

void Response::DebugPrint()
{
    cout << "version: " << _version << endl;
    cout << "state_code: " << _stateCode << endl;
    cout << "state_desc: " << _stateDesc << endl;
    cout << "header:" << endl;
    for (auto &[k, v] : _header)
    {
        cout << "\t" << k << ": " << v << endl;
    }
    cout << "content:" << endl;
    cout << "\tfd: " << _fd << endl;
    cout << "\tfile_type: " << _fileType << endl;
    cout << "\tfile_size: " << _fileSize << endl;
    cout << "\tcontent: " << _content << endl;
}

// 获取一个完整报文并作反序列化
bool EndPoint::GetReq()
{
    string buffer;
    char temp[1024];
    bool complete = false;
    while (true)
    {
        int n = recv(_sockfd, temp, sizeof(temp), 0);
        if (n > 0)
        {
            buffer.append(temp, n);
            if (_req.Deserialize(buffer))
            {
                complete = true;
                break;
            }
            else
                continue;
        }
        else if (n == 0)
        {
            LogMessage(ERROR, "recv error! client quit.");
            break;
        }
        else
        {
            LogMessage(ERROR, "recv error! %d:%s", errno, strerror(errno));
            break;
        }
    }
    return complete;
}

// 分析请求
int EndPoint::AnalyseReq()
{
    // 我们暂时只处理GET和POST方法
    if (_req._method == "GET")
    {
        // 截取查询字符串
        if (_req._uri.find('?') != string::npos)
        {
            Util::CutString(_req._uri, _req._url, _req._queryStr, "?");
        }
    }
    else if (_req._method == "POST")
    {
        // 检查首部Content-Length字段
        if (_req._header.find("Content-Length") == _req._header.end())
        {
            _resp._stateCode = 400;
            return 400;
        }
    }
    else
    {
        _resp._stateCode = 400;
        return 400;
    }

    // 设置资源的本地路径并检查资源
    _path = WEBROOT;
    _path += _req._url;
    if (CheckResource() != 200)
    {
        return _resp._stateCode;
    }

    // 执行CGI程序
    if (_req._cgi)
        return CGI();
    else
        return 200;
}

void EndPoint::BuildResp()
{
    // 填充响应行
    _resp._version = HTTP_VERSION;
    _resp._stateDesc = StateDescMap[_resp._stateCode];

    // 填充响应头
    if (_resp._stateCode == 200)
    {
        if (_req._cgi)
        {
            _resp._header["Content-Length"] = std::to_string(_resp._content.size());
        }
        else
        {
            _resp._header["Content-type"] = _resp._fileType;
            _resp._header["Content-Length"] = std::to_string(_resp._fileSize);
        }
    }
    else
    {
        // 返回错误页面
        std::stringstream ErrorPage;
        ErrorPage << WEBROOT << "/" << _resp._stateCode << "page.html";
        struct stat st;
        if (stat(ErrorPage.str().c_str(), &st) != -1)
        {
            _resp._header["Content-type"] = "text/html";
            _resp._header["Content-Length"] = std::to_string(st.st_size);
            _resp._fd = open(ErrorPage.str().c_str(), O_RDONLY);
            _resp._fileSize = st.st_size;
            _resp._fileType = "text/html";
        }
    }
}

void EndPoint::SendResp()
{
    string message = _resp.Serialize();
    int n = send(_sockfd, message.c_str(), message.size(), 0);
    if (n == -1)
    {
        LogMessage(ERROR, "send error! code:%d desc:%s", errno, strerror(errno));
    }

    if (_req._cgi && _resp._stateCode == 200)
    {
        // 发送CGI程序的执行结果
        n = send(_sockfd, _resp._content.c_str(), _resp._content.size(), 0);
        if (n == -1)
        {
            LogMessage(ERROR, "send error! code:%d desc:%s", errno, strerror(errno));
        }
    }
    else
    {
        // 发送静态网页内容（包括错误页面）
        n = sendfile(_sockfd, _resp._fd, nullptr, _resp._fileSize);
        if (n == -1)
        {
            LogMessage(ERROR, "sendfile error! code:%d desc:%s", errno, strerror(errno));
        }
    }
}

void EndPoint::DebugPrint()
{
    cout << "----------------request-------------------" << endl;
    _req.DebugPrint();
    cout << "local path: " << _path << endl;
    cout << "----------------------------------------" << endl;
    cout << "----------------response-----------------" << endl;
    _resp.DebugPrint();
    cout << "----------------------------------------" << endl;
}

// 检查请求的资源，并打开
int EndPoint::CheckResource()
{
    // stat获取文件属性
    struct stat st;
    if (stat(_path.c_str(), &st) == -1)
    {
        _resp._stateCode = 404;
        return 404;
    }

    mode_t mode = st.st_mode;
    // 如果是目录文件，重定向到当前目下的index页面
    // 注意：目录文件也有执行权限
    if (S_ISDIR(mode))
    {
        if (_path.back() != '/')
            _path += '/';
        _path += INDEX_PAGE;
        if (stat(_path.c_str(), &st) == -1)
        {
            _resp._stateCode = 404;
            return 404;
        }
    }
    // 如果是可执行程序，设置cgi标志
    else if ((mode & S_IXGRP) || (mode & S_IXOTH) || (mode & S_IXUSR))
    {
        _req._cgi = true;
        return 200;
    }

    // 如果不是CGI程序就要提前打开并获取属性信息方便后续读取
    // 打开文件
    int fd = open(_path.c_str(), O_RDONLY);
    if (fd == -1)
    {
        LogMessage(ERROR, "open error! code:%d desc:%s", errno, strerror(errno));
        _resp._stateCode = 500;
        return 500;
    }
    _resp._fd = fd;

    // 获取文件类型
    _resp._fileType = "text/html"; // 默认是html
    string fileSuffix;
    size_t dot = _path.rfind('.');
    if (dot != string::npos)
    {
        fileSuffix = _path.substr(dot);
        if (FileTypeMap.find(fileSuffix) != FileTypeMap.end())
        {
            _resp._fileType = FileTypeMap[fileSuffix];
        }
    }

    // 获取文件大小
    _resp._fileSize = st.st_size;
    return true;
}

int EndPoint::CGI()
{
    // 构建双向管道
    int input[2];
    int output[2];
    if (pipe(input) == -1 || pipe(output) == -1)
    {
        LogMessage(ERROR, "create pipe error! code:%d desc:%s", errno, strerror(errno));
        _resp._stateCode = 500;
        return 500;
    }

    // 创建子进程
    pid_t pid = fork();
    if (pid == -1)
    {
        LogMessage(ERROR, "fork error! code:%d desc:%s", errno, strerror(errno));
        _resp._stateCode = 500;
        // 如果创建子进程失败，返回前要关闭管道
        close(input[0]);
        close(input[1]);
        close(output[0]);
        close(output[1]);
        return 500;
    }
    else if (pid == 0)
    {
        // 子进程
        // 关闭管道不使用的读写端，并进行重定向
        close(input[0]);
        close(output[1]);
        dup2(input[1], 1);
        dup2(output[0], 0);

        // 通过环境变量传递必要信息（短信息）
        // setenv是线程安全的，而putenv不是线程安全的。在多线程环境下使用putenv可能会导致竞态条件和其他问题。
        setenv("METHOD", _req._method.c_str(), 1);
        if (_req._method == "GET")
        {
            setenv("QUERY_STRING", _req._queryStr.c_str(), 1);
        }
        else if (_req._method == "POST")
        {
            setenv("CONTENT_LENGTH", _req._header["Content-Length"].c_str(), 1);
        }

        // 进程程序替换，执行cig程序
        execl(_path.c_str(), _path.c_str(), nullptr);
        exit(errno); //正常情况下不执行
    }

    // 父进程

    // 关闭管道不使用的读写端
    close(input[1]);
    close(output[0]);

    // 如果是POST方法，要将请求体通过管道发送给子进程
    if (_req._method == "POST")
    {
        int total = 0;
        int size = _req._content.size();
        while (total < size)
        {
            int n = write(output[1], _req._content.c_str() + total, size - total);
            if (n == -1)
            {
                LogMessage(ERROR, "write error! code:%d desc:%s", errno, strerror(errno));
                break;
            }
            total += n;
        }
    }

    // 从管道中读取CGI程序的执行结果
    char buffer[1024];
    while (true)
    {
        int n = read(input[0], buffer, sizeof(buffer));
        if (n > 0)
        {
            _resp._content.append(buffer, n);
        }
        else
        {
            break;
        }
    }

    // 等待回收子进程，彻底关闭管道
    int stat = 0;
    waitpid(pid, &stat, 0);
    close(input[0]);
    close(output[1]);

    if (WIFEXITED(stat))
    {
        // 子进程正常退出，检查退出码
        switch (WEXITSTATUS(stat))
        {
        case CGI_OK:
            _resp._stateCode = 200;
            break;
        case CGI_BADREQUEST:
            _resp._stateCode = 400;
            break;
        default:
            break;
        }
    }
    else
    {
        // 子进程崩溃
        _resp._stateCode = 500;
        return 500;
    }
    return _resp._stateCode;
}